﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection;
using MugenInjection.Core;
using MugenInjection.Core.Components;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;
using MugenInjection.Interface.Components;
using Should;

namespace MugenInjectionTest.Infrastructure
{
    public class FakeComponent : Component
    {
        public int CountDispose { get; set; }

        public int CountLoad { get; set; }

        public int CountUnload { get; set; }

        public bool Single { get; set; }

        #region Overrides of Component

        /// <summary>
        /// Indicates whether the component is unique.
        /// </summary>
        public override bool IsSingle
        {
            get { return Single; }
        }

        /// <summary>
        /// Returns the component type. Need to find a component in the container.
        /// </summary>
        public override Type ComponentType
        {
            get { return typeof(FakeComponent); }
        }

        /// <summary>
        /// Occurs on load component.
        /// </summary>
        protected override void OnLoad()
        {
            CountLoad++;
        }

        /// <summary>
        /// Occurs on unload component.
        /// </summary>
        protected override void OnUnload()
        {
            CountUnload++;
        }

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public override IComponent Clone()
        {
            return new FakeComponent();
        }

        #endregion

        #region Overrides of DisposableObject

        /// <summary>
        /// Releases resources held by the object.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            CountDispose++;
            base.Dispose(disposing);
        }

        #endregion
    }

    [TestClass]
    public class ComponentWorkTest : TestBase
    {
        [TestMethod]
        public void EmptyContainerTest()
        {
            var container = new ComponentContainer();
            container.BindingActivatorComponent.ShouldBeNull();
            container.BindingManagerComponent.ShouldBeNull();
            TestUtils.Catch<NullReferenceException>(() => container.Add(new FakeComponent()));
            TestUtils.Catch<NullReferenceException>(() => container.Remove(new FakeComponent()));
            TestUtils.Catch<NullReferenceException>(container.RemoveAll<FakeComponent>);
            TestUtils.Catch<NullReferenceException>(() => container.Get<FakeComponent>());
            TestUtils.Catch<NullReferenceException>(() => container.GetAll<FakeComponent>());
            TestUtils.Catch<NullReferenceException>(() => container.GetEnumerator());
            TestUtils.Catch<NullReferenceException>(() => container.CloneAll());
            TestUtils.Catch<NullReferenceException>(() => container.IsContain(new FakeComponent()));
            TestUtils.Catch<NullReferenceException>(() => container.IsExist<FakeComponent>());
            TestUtils.Catch<NullReferenceException>(() => container.IsExist(typeof(FakeComponent)));
        }

        [TestMethod]
        public void InitContainerTest()
        {
            var container = new ComponentContainer();
            using (var mugenInjector = new MugenInjector(new DefaultInjectorSetting(), container))
            {
                container.BindingActivatorComponent.ShouldBeType<BindingActivatorComponent>();
                container.BindingManagerComponent.ShouldBeType<BindingManagerComponent>();
                container.Add(new FakeComponent());
                container.Remove(new FakeComponent());
                container.Get<FakeComponent>();
                container.GetAll<FakeComponent>();
                container.GetEnumerator();
                container.CloneAll();
                container.IsContain(new FakeComponent());
                container.IsExist<FakeComponent>();
                container.RemoveAll<FakeComponent>();
            }
        }

        [TestMethod]
        public void InjectorMustHaveOnlyOneContainerTest()
        {
            using (var injector = GetInjector())
            {
                var container = new ComponentContainer();
                TestUtils.Catch<NotSupportedException>(() => container.Initialize(injector));
            }
        }

        [TestMethod]
        public void SingleComponentTest()
        {
            using (var injector = GetInjector())
            {
                var component = new FakeComponent { Single = false };
                injector.Components.Add(component);
                component = new FakeComponent { Single = true };
                TestUtils.Catch<ArgumentException>(() => injector.Components.Add(component));
            }
        }

        [TestMethod]
        public void DuplicateComponentTest()
        {
            using (var injector = GetInjector())
            {
                var component = new FakeComponent { Single = false };
                injector.Components.Add(component);
                TestUtils.Catch<ArgumentException>(() => injector.Components.Add(component));
            }
        }

        [TestMethod]
        public void SinlgeComponentNotUnloadOnlyReplaceTest()
        {
            using (var injector = GetInjector())
            {
                var bindingActivatorComponent = injector.Components.BindingActivatorComponent;
                var bindingManagerComponent = injector.Components.BindingManagerComponent;

                injector.Components.IsContain(bindingActivatorComponent).ShouldBeFalse();
                injector.Components.IsContain(bindingManagerComponent).ShouldBeFalse();

                TestUtils.Catch<NotSupportedException>(() => injector.Components.BindingActivatorComponent.Unload());
                TestUtils.Catch<NotSupportedException>(() => injector.Components.BindingManagerComponent.Unload());

                bindingActivatorComponent.Injector.ShouldNotBeNull();
                bindingManagerComponent.Injector.ShouldNotBeNull();

                injector.Components.DisposeComponentAfterUnload = true;

                bindingActivatorComponent.IsDisposed.ShouldBeFalse();
                bindingManagerComponent.IsDisposed.ShouldBeFalse();

                injector.Components.BindingActivatorComponent = new BindingActivatorComponent();
                injector.Components.BindingManagerComponent = new BindingManagerComponent();

                injector.Components.IsContain(bindingActivatorComponent).ShouldBeFalse();
                injector.Components.IsContain(bindingManagerComponent).ShouldBeFalse();
                bindingActivatorComponent.Injector.ShouldBeNull();
                bindingManagerComponent.Injector.ShouldBeNull();
                bindingActivatorComponent.IsDisposed.ShouldBeTrue();
                bindingManagerComponent.IsDisposed.ShouldBeTrue();
            }
        }

        [TestMethod]
        public void TestMainComponentWork()
        {
            using (var injector = GetInjector())
            {
                injector.Components.IsExist<FakeComponent>().ShouldBeFalse();
                var component = new FakeComponent();

                component.CountLoad.ShouldEqual(0);
                injector.Components.Add(component);
                component.CountLoad.ShouldEqual(1);
                injector.Components.IsExist<FakeComponent>().ShouldBeTrue();
                injector.Components.IsContain(component).ShouldBeTrue();

                var fakeComponent = injector.Components.Get<FakeComponent>();
                fakeComponent.ShouldEqual(component);

                component.CountUnload.ShouldEqual(0);
                injector.Components.Remove(fakeComponent).ShouldBeTrue();
                injector.Components.Remove(fakeComponent).ShouldBeFalse();
                component.CountUnload.ShouldEqual(1);
                injector.Components.IsExist<FakeComponent>().ShouldBeFalse();
                injector.Components.IsContain(component).ShouldBeFalse();

                TestUtils.Catch<KeyNotFoundException>(() => injector.Components.Get<FakeComponent>());


                var componentMass = new List<IComponent>();
                for (int i = 0; i < 1000; i++)
                {
                    fakeComponent = new FakeComponent();
                    injector.Components.Add(fakeComponent);
                    componentMass.Add(fakeComponent);
                }
                var components = componentMass.ToArray();
                foreach (var comp in injector.Components.GetAll<FakeComponent>())
                {
                    componentMass.Contains(comp).ShouldBeTrue();
                    componentMass.Remove(comp).ShouldBeTrue();
                }
                injector.Components.RemoveAll<FakeComponent>();
                injector.Components.IsExist<FakeComponent>().ShouldBeFalse();
                foreach (FakeComponent component1 in components)
                {
                    component1.CountUnload.ShouldEqual(1);
                    injector.Components.IsContain(component1).ShouldBeFalse();
                }
            }
        }

        [TestMethod]
        public void ComponentLoadUnloadTest()
        {
            using (var injector = GetInjector())
            {
                injector.Components.DisposeComponentAfterUnload = false;
                var fakeComp = new FakeComponent();

                fakeComp.Load(injector);
                fakeComp.Injector.ShouldEqual(injector);
                fakeComp.CountLoad.ShouldEqual(1);
                injector.Components.IsContain(fakeComp).ShouldBeTrue();
                fakeComp.IsLoaded.ShouldBeTrue();

                fakeComp.Unload();
                fakeComp.Injector.ShouldBeNull();
                fakeComp.CountUnload.ShouldEqual(1);
                injector.Components.IsContain(fakeComp).ShouldBeFalse();
                fakeComp.IsLoaded.ShouldBeFalse();

                fakeComp.Load(injector, false);
                fakeComp.Injector.ShouldEqual(injector);
                fakeComp.CountLoad.ShouldEqual(2);
                injector.Components.IsContain(fakeComp).ShouldBeFalse();
                fakeComp.IsLoaded.ShouldBeTrue();

                fakeComp.Unload();
                fakeComp.Injector.ShouldBeNull();
                fakeComp.CountUnload.ShouldEqual(2);
                injector.Components.IsContain(fakeComp).ShouldBeFalse();
                fakeComp.IsLoaded.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void DisposeComponentWithContainerTest()
        {
            //DisposeComponentAfterUnload is true
            using (var injector = GetInjector())
            {
                var fakeComp = new FakeComponent();
                fakeComp.Load(injector);

                injector.Components.DisposeComponentAfterUnload = true;
                injector.Dispose();
                fakeComp.IsDisposed.ShouldBeTrue();
                fakeComp.CountDispose.ShouldEqual(1);
                fakeComp.CountUnload.ShouldEqual(1);
                fakeComp.Injector.ShouldBeNull();
            }

            //DisposeComponentAfterUnload is false
            using (var injector = GetInjector())
            {
                var fakeComp = new FakeComponent();
                fakeComp.Load(injector);

                injector.Components.DisposeComponentAfterUnload = false;
                injector.Dispose();
                fakeComp.IsDisposed.ShouldBeFalse();
                fakeComp.CountDispose.ShouldEqual(0);
                fakeComp.CountUnload.ShouldEqual(1);
                fakeComp.Injector.ShouldBeNull();
            }
        }
    }
}